﻿#include <system/render_system.h>
#include <common_header.h>
#include <gfx/dx12/core.h>
#include <utl/input.h>
#include <gfx/dx12/helper.h>
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <gui/Dx12GlfwGui.h>

class swapchain_resize_callback;

namespace primal
{
	constexpr u32 RENDER_FRAME_COUNT{ 4u };
	constexpr u32 RENDER_FRAME_COUNT_1{ RENDER_FRAME_COUNT - 1u };

	constexpr D3D12_CLEAR_VALUE depth_clear_value
	{
		d3dx::default_formats.depth_stencil,
		{ 1.0f,static_cast<u8>(0) }
	};

	enum RENDER_TASK_TYPE
	{
		UI_TASK_TYPE,
		SCENE_TASK_TYPE,
		MAX_TASK_TYPE
	};

	enum G_BUFFER_TYPE
	{
		G_BUFFER_TYPE_POSITION,
		G_BUFFER_TYPE_NORMAL,
		G_BUFFER_TYPE_COLOR,
		G_BUFFER_TYPE_MAX,
	};

	struct GRS_VERTEX
	{
		DirectX::XMFLOAT4 m_vtPos;
		DirectX::XMFLOAT4 m_vtColor;
	};


	struct
	{
		//swapchain_resize_callback* swap_callback;

		struct
		{
			DirectX::XMUINT2 real_size;
			d3dx::swapchain_desc create_info;
			gfx::IDXGISwapChainN* entity;
			u32 interval;
			u32 flag;
		}swapchain;

		DirectX::XMUINT2 swapchain_size;

		struct
		{
			ID3D12Resource* color_buffer;
			gfx::descriptor_handle render_handle;
		}ui_frames[RENDER_FRAME_COUNT];

		DirectX::XMUINT2 render_size;

		struct
		{
			DirectX::XMUINT2 real_size;
			ID3D12Resource* color_buffer;
			ID3D12Resource* color_vertices;
			D3D12_VERTEX_BUFFER_VIEW color_vertex_view;
			ID3D12Resource* color_indces;
			D3D12_INDEX_BUFFER_VIEW color_index_view;
			ID3D12Resource* g_buffers[G_BUFFER_TYPE_MAX];
			ID3D12Resource* depth_buffer;
			gfx::descriptor_handle color_handle;
			gfx::descriptor_handle render_handle;
			gfx::descriptor_handle g_render_handles[G_BUFFER_TYPE_MAX];
			gfx::descriptor_handle g_shader_handles[G_BUFFER_TYPE_MAX];
			gfx::descriptor_handle depth_handle;
			D3D12_VIEWPORT viewport;
			D3D12_RECT sciss;
			ID3D12Resource* camera_buffer;
			ID3D12Resource* light_buffer;

		}render_frame;

		struct
		{
			ID3D12CommandAllocator* cmd_allocator;
			gfx::ID3D12GraphicsCommandListN* cmd_list[MAX_TASK_TYPE];
			u64 local_fence_value;
			HANDLE wait_object;
		}command_frames[RENDER_FRAME_COUNT];

		u32 ui_frame_index;
		u32 render_frame_index;
		u32 command_frame_index;

		u64 fence_value;
		ID3D12Fence* fence;
		HANDLE flush_object;

		d3dx::resource_barrier1<3u> bar;

		bool is_init;
	}global{};

	struct
	{
		ID3D12Resource* vertex_data;
	}test_data{};

	inline void wait_frame(HANDLE e, u64 fv)
	{
		if(global.fence->GetCompletedValue() < fv)
		{
			global.fence->SetEventOnCompletion(fv, e);
			WaitForSingleObject(e, INFINITE);
		}
	}

	inline void flush()
	{
		wait_frame(global.flush_object, global.fence_value);
	}

	inline void render_resize(DirectX::XMUINT2 size)
	{
		if(size.x == 0 || size.y == 0)
		{
			return;
		}

		global.render_frame.viewport.Width = (float)size.x;
		global.render_frame.viewport.Height = (float)size.y;
		global.render_frame.sciss.right = (long)size.x;
		global.render_frame.sciss.bottom = (long)size.y;


		global.render_frame.real_size = size;
		flush();
		gfx::release(global.render_frame.color_buffer);
		gfx::release(global.render_frame.depth_buffer);
		d3dx::resource_desc color_desc{};
		color_desc.as_texture_2d(size.x, size.y, d3dx::default_formats.render_target, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
		DXASSERT(gfx::device()->CreateCommittedResource(&d3dx::heap_properties.default_heap, D3D12_HEAP_FLAG_NONE, &color_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, DXUUID(global.render_frame.color_buffer)));

		gfx::device()->CreateRenderTargetView(global.render_frame.color_buffer, nullptr, global.render_frame.render_handle.cpu);
		d3dx::view_desc vd{};
		gfx::device()->CreateShaderResourceView(global.render_frame.color_buffer, vd.as_srv_texture_2d(), global.render_frame.color_handle.cpu);

		d3dx::resource_desc depth_desc{};
		depth_desc.as_texture_2d(size.x, size.y, d3dx::default_formats.depth_stencil, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
		DXASSERT(gfx::device()->CreateCommittedResource(&d3dx::heap_properties.default_heap, D3D12_HEAP_FLAG_NONE, &depth_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &depth_clear_value, DXUUID(global.render_frame.depth_buffer)));
		d3dx::view_desc vd_depth{};
		gfx::device()->CreateDepthStencilView(global.render_frame.depth_buffer, vd_depth.as_dsv_texture_2d(d3dx::default_formats.depth_stencil), global.render_frame.depth_handle.cpu);
	}

	void swapchain_resize(u32 width, u32 height)
	{
		global.swapchain.real_size = { width,height };
		flush();
		for(auto& item : global.ui_frames)
		{
			gfx::release(item.color_buffer);
		}

		DXASSERT(global.swapchain.entity->ResizeBuffers(RENDER_FRAME_COUNT, width, height, global.swapchain.create_info.Format, global.swapchain.create_info.Flags));

		for(u32 i{}; i < RENDER_FRAME_COUNT; ++i)
		{
			auto& frame{ global.ui_frames[i] };
			DXASSERT(global.swapchain.entity->GetBuffer(i, DXUUID(frame.color_buffer)));
			gfx::device()->CreateRenderTargetView(frame.color_buffer, nullptr, frame.render_handle.cpu);
		}

		global.ui_frame_index = 0u;
	}

	inline void present_swapchain()
	{
		DXGI_PRESENT_PARAMETERS par{};
		global.swapchain.entity->Present1(global.swapchain.interval, global.swapchain.flag, &par);
	}

	inline void render_ui(gfx::ID3D12GraphicsCommandListN* list)
	{

	}

	inline void render_scence(gfx::ID3D12GraphicsCommandListN* list)
	{

	}
}

inline void on_swapchain_resize(input::input_code code, const input::input_value& value)
{
	if(code == input::window_resize)
	{
		primal::global.swapchain_size = { (u32)value.current.ix,(u32)value.current.iy };
	}
}

void render_preview(const primal::FontList& icon_ft, bool* life)
{
	//auto pad{ ImGui::GetStyle().WindowPadding };
	//ImGui::GetStyle().WindowPadding = {};
	ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{});
	if(ImGui::Begin(u8"预览", life))
	{
		ImVec2 vp = ImGui::GetContentRegionAvail();
		primal::global.render_size = { static_cast<u32>(vp.x), static_cast<u32>(vp.y) };
		ImGui::Image((ImTextureID)primal::global.render_frame.color_handle.gpu.ptr, vp);

		if(ImGui::BeginPopupContextItem("my popup1"))
		{
			ImGui::Selectable("Set to zero");
			ImGui::Selectable("Set to zero");
			ImGui::Image(ImGui::GetIO().Fonts->TexID, { 100,100 });
			ImGui::EndPopup();
		}
	}
	ImGui::End();
	ImGui::PopStyleVar();
	//ImGui::GetStyle().WindowPadding = pad;
}

#include <res/icon_font_def_u8.h>

bool primal::render_system_initialize(GLFWwindow* window)
{
	if(global.is_init)
	{
		return global.is_init;
	}

	{
		gui::register_window(render_preview, u8"预览", iconft::e93b);

		for(auto& item : global.ui_frames)
		{
			gfx::rtv().allocator(item.render_handle);
		}

		gfx::rtv().allocator(global.render_frame.render_handle);
		gfx::srv().allocator(global.render_frame.color_handle);
		gfx::dsv().allocator(global.render_frame.depth_handle);

		for(auto& item : global.command_frames)
		{
			DXASSERT(gfx::device()->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, DXUUID(item.cmd_allocator)));
			DXASSERT(gfx::device()->CreateCommandList1(0u, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_FLAG_NONE, DXUUID(item.cmd_list[UI_TASK_TYPE])));
			DXASSERT(gfx::device()->CreateCommandList1(0u, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_FLAG_NONE, DXUUID(item.cmd_list[SCENE_TASK_TYPE])));
			item.wait_object = CreateEventA(nullptr, FALSE, FALSE, nullptr);
			assert(item.wait_object != nullptr);
		}

		DXASSERT(gfx::device()->CreateFence(global.fence_value, D3D12_FENCE_FLAG_NONE, DXUUID(global.fence)));

		global.flush_object = CreateEventA(nullptr, FALSE, FALSE, nullptr);
		assert(global.flush_object != nullptr);

		global.swapchain.flag = DXGI_PRESENT_ALLOW_TEARING;

		global.swapchain.create_info.init_unvsync(0u, 0u, RENDER_FRAME_COUNT);
		gfx::ComPtr<IDXGISwapChain1> temp_swapchain{};

		gfx::factory()->CreateSwapChainForHwnd
		(
			gfx::direct_queue().Get(),
			glfwGetWin32Window(window),
			&global.swapchain.create_info,
			nullptr,
			nullptr,
			temp_swapchain.GetAddressOf()
		);

		temp_swapchain->QueryInterface(&global.swapchain.entity);

		int w{}, h{};
		glfwGetWindowSize(window, &w, &h);

		global.swapchain_size = { (u32)w, (u32)h };
		swapchain_resize((u32)w, (u32)h);

		input::register_callback(on_swapchain_resize);

		global.render_frame.viewport = { 0.0f, 0.0f, .0f, .0f, D3D12_MIN_DEPTH, D3D12_MAX_DEPTH };

		render_resize({ 200u,200u });

		GRS_VERTEX stTriangleVertices[] =
		{
			{ { -1.0f ,  1.0f,  0.0f ,1.0f }, { 0.0f, 0.0f, 0.5f, 1.0f } },
			{ {  1.0f ,  1.0f , 0.0f ,1.0f }, { 1.0f, 0.0f, 0.5f, 1.0f } },
			{ { -1.0f , -1.0f , 0.0f ,1.0f }, { 0.0f, 1.0f, 0.5f, 1.0f } },
			{ {  1.0f , -1.0f , 0.0f ,1.0f }, { 1.0f, 1.0f, 0.5f, 1.0f } },
		};

		u32 indces[] =
		{
			0u,
			1u,
			2u,
			2u,
			1u,
			3u
		};

		d3dx::resource_desc vert_desc{};
		vert_desc.as_buffer(sizeof(stTriangleVertices));
		DXASSERT(gfx::device()->CreateCommittedResource(&d3dx::heap_properties.upload_heap, D3D12_HEAP_FLAG_NONE, &vert_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, DXUUID(global.render_frame.color_vertices)));

		d3dx::resource_desc indces_desc{};
		indces_desc.as_buffer(sizeof(indces));
		DXASSERT(gfx::device()->CreateCommittedResource(&d3dx::heap_properties.upload_heap, D3D12_HEAP_FLAG_NONE, &indces_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, DXUUID(global.render_frame.color_indces)));

		void* vert_data_map{};
		global.render_frame.color_vertices->Map(0u, nullptr, &vert_data_map);
		memcpy(vert_data_map, stTriangleVertices, sizeof(stTriangleVertices));
		global.render_frame.color_vertices->Unmap(0u, nullptr);

		void* inde_data_map{};
		global.render_frame.color_indces->Map(0u, nullptr, &inde_data_map);
		memcpy(inde_data_map, indces, sizeof(indces));
		global.render_frame.color_indces->Unmap(0u, nullptr);

		auto& vert_view{ global.render_frame.color_vertex_view };
		vert_view.BufferLocation = global.render_frame.color_vertices->GetGPUVirtualAddress();
		vert_view.SizeInBytes = sizeof(stTriangleVertices);
		vert_view.StrideInBytes = sizeof(GRS_VERTEX);

		auto& index_view{ global.render_frame.color_index_view };
		index_view.BufferLocation = global.render_frame.color_indces->GetGPUVirtualAddress();
		index_view.SizeInBytes = sizeof(indces);
		index_view.Format = DXGI_FORMAT_R32_UINT;

		auto& p{ gfx::get_pipeline(gfx::Debug) };
	}



	global.is_init = true;
	return global.is_init;
}

inline bool operator==(const DirectX::XMUINT2& a, const DirectX::XMUINT2& b)
{
	return (a.x == b.x) && (a.y == b.y);
}

inline bool operator!=(const DirectX::XMUINT2& a, const DirectX::XMUINT2& b)
{
	return (a.x != b.x) || (a.y != b.y);
}



void primal::render_system_render()
{
	if(global.swapchain.real_size != global.swapchain_size)
	{
		swapchain_resize(global.swapchain_size.x, global.swapchain_size.y);
	}
	if(global.render_size != global.render_frame.real_size)
	{
		render_resize(global.render_size);
	}
	auto& command_frame{ global.command_frames[global.command_frame_index] };

	wait_frame(command_frame.wait_object, command_frame.local_fence_value);
	DXASSERT(command_frame.cmd_allocator->Reset());

	auto& pip_info{ gfx::get_pipeline(gfx::Debug) };
	command_frame.cmd_list[SCENE_TASK_TYPE]->Reset(command_frame.cmd_allocator, pip_info.pipeline);
	auto rlist{ command_frame.cmd_list[SCENE_TASK_TYPE] };
	rlist->SetGraphicsRootSignature(pip_info.root_signature);
	rlist->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	global.bar.add(global.render_frame.color_buffer, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
	global.bar.apply(rlist);

	rlist->OMSetRenderTargets(1u, &global.render_frame.render_handle.cpu, FALSE, nullptr);
	rlist->ClearDepthStencilView(global.render_frame.depth_handle.cpu, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
	rlist->RSSetViewports(1u, &global.render_frame.viewport);
	rlist->RSSetScissorRects(1u, &global.render_frame.sciss);

	rlist->IASetVertexBuffers(0u, 1u, &global.render_frame.color_vertex_view);
	rlist->IASetIndexBuffer(&global.render_frame.color_index_view);
	rlist->DrawIndexedInstanced(6u, 1u, 0u, 0, 0u);

	global.bar.add(global.render_frame.color_buffer, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
	global.bar.apply(rlist);

	command_frame.cmd_list[SCENE_TASK_TYPE]->Close();

	{
		command_frame.cmd_list[UI_TASK_TYPE]->Reset(command_frame.cmd_allocator, nullptr);

		//TODO::

		{
			present_swapchain();
			++global.ui_frame_index;
			global.ui_frame_index &= RENDER_FRAME_COUNT_1;
			auto& ui_frame{ global.ui_frames[global.ui_frame_index] };

			global.bar.add(ui_frame.color_buffer, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
			global.bar.apply(command_frame.cmd_list[UI_TASK_TYPE]);
			command_frame.cmd_list[UI_TASK_TYPE]->OMSetRenderTargets(1u, &ui_frame.render_handle.cpu, FALSE, nullptr);

			auto heap = gfx::srv().Get();
			command_frame.cmd_list[UI_TASK_TYPE]->SetDescriptorHeaps(1, &heap);

			gui::set_render_list(command_frame.cmd_list[UI_TASK_TYPE]);
			gui::render();

			global.bar.add(ui_frame.color_buffer, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
			global.bar.apply(command_frame.cmd_list[UI_TASK_TYPE]);
		}

		command_frame.cmd_list[UI_TASK_TYPE]->Close();
	}

	++global.fence_value;
	gfx::direct_queue()->ExecuteCommandLists(2u, (ID3D12CommandList**)(command_frame.cmd_list));
	gfx::direct_queue()->Signal(global.fence, global.fence_value);
	command_frame.local_fence_value = global.fence_value;

	++global.command_frame_index;
	global.command_frame_index &= RENDER_FRAME_COUNT_1;
}
